# 坐标下降法，Lasso回归
# In[]
import numpy as np 
from sklearn.linear_model import Lasso
import matplotlib.pyplot as plt 
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split,cross_val_score,cross_validate
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score,mean_squared_error

class my_Lasso():

    def __init__(self,alpha=1.0,max_iter=1000,tol=1e-4):
        self.alpha = alpha
        self.max_iter = max_iter
        self.tol = tol
        self.errs = []
        
    def fit(self,X,y):
        X = np.array(X)
        # 将截距整合到数据中
        X = np.column_stack((np.ones((X.shape[0])),X))
        y = np.array(y)
        # coef包含了截距
        self.coef_ = np.zeros((X.shape[1]))
        self.a_ = np.sum(X ** 2,axis=0) * 2
        for i in range(self.max_iter):
            for j in range(X.shape[1]):
                # 计算cj
                xj = X[:,j]
                x_j = np.delete(X,j,axis=1)
                w_j = np.delete(self.coef_,j)
                cj = 2 * np.sum(xj * (y - w_j.dot(x_j.T)))
                aj = self.a_[j]
                if cj < -self.alpha:
                    self.coef_[j] = (cj + self.alpha) / aj
                elif (-self.alpha <= cj) and (cj <= self.alpha):
                    self.coef_[j] = 0
                elif cj > self.alpha:
                    self.coef_[j] = (cj - self.alpha)/aj
            # 更新完一轮，计算误差
            err = mean_squared_error(self.predict(np.delete(X,0,axis=1)),y)
            self.errs.append(err)
            if (i > 1) and (abs(self.errs[-2] - err) < self.tol):
                break

    def score(self,X,y):
        ypred = self.predict(X)
        return mean_squared_error(y,ypred)

    def predict(self,X):
        X = np.column_stack((np.ones(X.shape[0]),X))
        return self.coef_.dot(X.T)

X,y = load_boston(True)
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=4)
scx = StandardScaler()
scx.fit(X_train)
X_train = scx.transform(X_train)
X_test = scx.transform(X_test)

errs = []
for i in range(0,1000,10):
    las = my_Lasso(alpha=i,max_iter=3000,tol=0.1)
    las.fit(X_train,y_train)
    y_pred = las.predict(X_test)
    errs.append(mean_squared_error(y_pred,y_test))
# print('自实现Lasso的权重:',las.coef_[1:])
# print('自实现Lasso的截距：',las.coef_[0])
# print('自实现Lasso预测数据的MSE',mean_squared_error(y_pred,y_test))
plt.plot(errs,label='MyLasso')
# plt.ylim(0,25)

errs = []
for i in np.arange(0.01,1,0.01):
    las = Lasso(alpha=i)
    las.fit(X_train,y_train)
    y_pred = las.predict(X_test)
    errs.append(mean_squared_error(y_pred,y_test))
# print('sklearn的Lasso的权重：',las.coef_)
# print('sklearn的Lasso的权重：',las.intercept_)
# print('sklearn的Lasso预测的MSE：',mean_squared_error(y_pred,y_test))
plt.plot(errs,label='sklearnLasso')
plt.xlabel('alpha times')
plt.ylabel('MSE')
plt.legend()
plt.show()



#%%

las = my_Lasso(alpha=60,tol=0.1)
las.fit(X_train,y_train)
y_pred = las.predict(X_test)
print('自实现Lasso的权重:',las.coef_[1:])
print('自实现Lasso的截距：',las.coef_[0])
print('自实现Lasso预测数据的MSE',mean_squared_error(y_pred,y_test))
plt.plot(las.errs,label='MyLasso alpha=1')
plt.xlabel('iter number')
plt.ylabel('MSE')
plt.legend()
plt.show()

las = Lasso(alpha=0.001)
las.fit(X_train,y_train)
y_pred = las.predict(X_test)
print('sklearn的Lasso的权重：',las.coef_)
print('sklearn的Lasso的权重：',las.intercept_)
print('sklearn的Lasso预测的MSE：',mean_squared_error(y_pred,y_test))


#%%
train_errs = []
test_errs = []
for i in np.arange(2,0.01,-0.01):
    lasso = Lasso(alpha=i)
    scores = cross_validate(lasso,X,y,return_train_score=True,scoring=('neg_mean_squared_error'))
    print(scores.keys())
    train = -scores['train_score'].mean()
    test = -scores['test_score'].mean()
    train_errs.append(train)
    test_errs.append(test)

plt.plot(np.arange(2,0.01,-0.01),train_errs,label='train')
plt.plot(np.arange(2,0.01,-0.01),test_errs,label='test')
plt.legend()
plt.show()
    

#%%
from sklearn.model_selection import GridSearchCV
import pandas as pd 
lasso = Lasso()
clf = GridSearchCV(lasso,{'alpha':np.arange(0.001,0.1,0.005)},refit=True,return_train_score=True)
clf.fit(X_train,y_train)
print(sorted(clf.cv_results_))
results = pd.DataFrame.from_dict(clf.cv_results_)
clf.be
# 保存到文件
with open('第四次作业/cv_result.csv','w') as f:
    results.to_csv(f)
print(clf.best_params_)



